﻿// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;

namespace SnapObjects.Data.Oracle
{
    internal class OracleSqlColumnSchemaProvider : SqlColumnSchemaProvider
    {
        public OracleSqlColumnSchemaProvider(DataContext context) : base(context)
        {
        }

        protected override List<DbColumnInfo> CreateDbColumnInfo(IDataReader reader)
        {
            var columnInfo = base.CreateDbColumnInfo(reader);

            this.SetUniqueColumn(columnInfo);

            this.SetIdentityColumn(columnInfo);

            return columnInfo;
        }

        protected override bool IsIdentity(DataRow row, IDataReader reader)
        {
            return false;
        }

        protected override bool IsReadOnly(DataRow row, IDataReader reader)
        {
            var column = row.Table.Columns["isreadonly"];

            var value = row.ItemArray[column.Ordinal];

            return this.GetSchemaItemValue<bool>(value);
        }

        protected override string ColumnDataTypeName(DataRow row, IDataReader reader)
        {
            var ordinal = this.ColumnOrdinal(row, reader);

            if (reader != null)
            {
                return reader.GetDataTypeName(ordinal);
            }

            return null;
        }

        protected override bool IsAliased(DataRow row, IDataReader reader)
        {
            var column = row.Table.Columns["isaliased"];

            var value = row.ItemArray[column.Ordinal];

            return this.GetSchemaItemValue<bool>(value);
        }

        protected override IList<string> GetNotInWhereType()
        {
            return new List<string>() { "clob", "nclob", "long" };
        }

        private void SetUniqueColumn(List<DbColumnInfo> columnInfo)
        {
            if (columnInfo.Any(m => m.IsPrimaryKey))
            {
                return;
            }

            var column = columnInfo.FirstOrDefault(m => !String.IsNullOrWhiteSpace(m.Table));

            if (column != null)
            {
                var uniqueColumnName = this.GetUniqueColumn(column.Table);

                if (!String.IsNullOrWhiteSpace(uniqueColumnName))
                {
                    var uniqueColumn = columnInfo.FirstOrDefault(
                        m => uniqueColumnName.Equals(m.ColumnName, StringComparison.OrdinalIgnoreCase));

                    if (uniqueColumn != null)
                    {
                        uniqueColumn.IsUnique = true;
                    }
                }
            }
        }

        private string GetUniqueColumn(string tableName)
        {
            var sql = @"
select col.column_name 
  from user_indexes ind
 inner join user_ind_columns col
         on ind.index_name = col.index_name
        and ind.table_name = col.table_name
        and ind.uniqueness = 'UNIQUE'
 where ind.table_name = upper('{0}')";

            var name = _dataContext.SqlExecutor.Scalar<string>(String.Format(sql, tableName));

            return name;
        }

        private void SetIdentityColumn(List<DbColumnInfo> columnInfo)
        {
            var identityColumns = new Dictionary<string, IList<string>>();

            foreach (var column in columnInfo)
            {
                if (String.IsNullOrWhiteSpace(column.Table) || String.IsNullOrWhiteSpace(column.BaseColumnName))
                {
                    continue;
                }

                var fullName = column.Table;

                if (!String.IsNullOrWhiteSpace(column.TableSchema))
                {
                    fullName = column.TableSchema + "." + column.Table;
                }

                if (!identityColumns.TryGetValue(fullName, out var identities))
                {
                    identities = this.GetIdentityColumns(column.TableSchema, column.Table);

                    identityColumns.Add(fullName, identities);
                }

                var identity = identities.FirstOrDefault(m => column.BaseColumnName.Equals(m));

                if (identity != null)
                {
                    column.IsIdentity = true;
                }
            }
        }

        private IList<string> GetIdentityColumns(string owner, string table)
        {
            var queryTable = "user_tab_identity_cols";
            var whereClause = $" where table_name = '{table}'";

            if (!String.IsNullOrWhiteSpace(owner))
            {
                queryTable = "all_tab_identity_cols";

                whereClause += $" and owner = '{owner}'"; 
            }

            var sql = $"select column_name from {queryTable} {whereClause}";

            var result = _dataContext.SqlExecutor.Select<DynamicModel>(sql);

            var names = result.Select(m => m.GetValue<string>(0)).ToList();

            return names;
        }
    }
}
